import configparser
import sys
from typing import Dict, Any
from obs import ObsClient
import os
import traceback
import time
import logging
from tqdm import tqdm

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


class ProgressTracker:
    def __init__(self, total_size):
        self.pbar = tqdm(total=total_size, unit='B', unit_scale=True, desc="上传进度")
        self.uploaded = 0

    def update(self, transferred, total, elapsed, **kwargs):
        """
        OBS SDK实际回调会传入4个参数：
        - transferred: 已传输字节数
        - total: 总字节数
        - elapsed: 已用时间(秒)
        - kwargs: 其他参数
        """
        delta = transferred - self.uploaded
        self.pbar.update(delta)
        self.uploaded = transferred

    def close(self):
        self.pbar.close()


def upload_to_obs(
        bucket_name,
        osb_save_path,
        local_file_path,
        access_key_id,
        secret_access_key,
        server,
        max_retries=10,
        retry_delay=1,
        part_size=10 * 1024 * 1024,
        task_num=5,
        enable_checkpoint=True
):
    """带进度显示的文件上传函数"""
    # 验证本地文件
    if not os.path.isfile(local_file_path):
        raise FileNotFoundError(f"文件不存在: {local_file_path}")

    file_size = os.path.getsize(local_file_path)
    progress = ProgressTracker(file_size)  # 初始化进度条

    obs_client = ObsClient(access_key_id=access_key_id, secret_access_key=secret_access_key, server=server)
    for attempt in range(max_retries):
        try:
            logger.info(f"尝试上传 (第 {attempt + 1}/{max_retries} 次)...")

            # 关键修改：添加进度回调
            object_key = osb_save_path + '/' + os.path.basename(local_file_path)
            # print(object_key)
            resp = obs_client.uploadFile(
                bucketName=bucket_name,
                objectKey=object_key,
                uploadFile=local_file_path,
                partSize=part_size,
                taskNum=task_num,
                enableCheckpoint=enable_checkpoint,
                encoding_type='url',
                progressCallback=progress.update  # 进度回调
            )

            if resp.status < 300:
                progress.close()
                logger.info(f"上传成功! | 文件大小: {file_size / 1024 / 1024:.2f} MB")
                logger.info(f"Request ID: {resp.requestId}")
                return True
            else:
                logger.warning(f"上传失败 (状态码: {resp.status})")

        except Exception as e:
            logger.error(f"尝试 {attempt + 1} 失败: {str(e)}")
            if attempt == max_retries - 1:  # 最后一次失败时打印堆栈
                logger.debug(traceback.format_exc())

        # 等待重试
        if attempt < max_retries - 1:
            logger.info(f"{retry_delay}秒后重试...")
            time.sleep(retry_delay)

    progress.close()
    logger.error("上传最终失败")
    return False


def load_config() -> Dict[str, Any]:
    """从配置文件加载所有设置"""
    config = configparser.ConfigParser()

    # 默认配置
    default_config = {
        'FileSettings': {
            'allowed_extensions': '.jar, .war, .txt, .pdf, .doc, .docx, .xls, .xlsx, .jpg, .png',
            'min_size_kb': '100'
        },
        'OSSSettings': {
            'access_key_id': '',
            'secret_access_key': '',
            'server': '',
            'bucket_name': 'czhy-zb',
            'osb_save_path': '',
            'part_size': '5242880',  # 5MB in bytes
            'task_num': '5',
            'max_retries': '1000',
            'retry_delay': '1'
        }
    }

    # 如果配置文件不存在，则创建默认配置
    if not os.path.exists('config.ini'):
        with open('config.ini', 'w', encoding='utf-8') as f:
            config.read_dict(default_config)
            config.write(f)
        logger.info("已创建默认配置文件 config.ini")

    # 读取配置
    config.read('config.ini', encoding='utf-8')

    try:
        # 文件设置
        ext_str = config.get('FileSettings', 'allowed_extensions')
        file_settings = {
            'allowed_extensions': {ext.strip().lower() for ext in ext_str.split(',') if ext.strip()},
            'min_size_kb': config.getint('FileSettings', 'min_size_kb')
        }

        # OSS设置
        oss_settings = {
            'access_key_id': config.get('OSSSettings', 'access_key_id'),
            'secret_access_key': config.get('OSSSettings', 'secret_access_key'),
            'server': config.get('OSSSettings', 'server'),
            'bucket_name': config.get('OSSSettings', 'bucket_name'),
            'osb_save_path': config.get('OSSSettings', 'osb_save_path'),
            'part_size': config.getint('OSSSettings', 'part_size'),
            'task_num': config.getint('OSSSettings', 'task_num'),
            'max_retries': config.getint('OSSSettings', 'max_retries'),
            'retry_delay': config.getint('OSSSettings', 'retry_delay')
        }

        return {
            'file_settings': file_settings,
            'oss_settings': oss_settings
        }

    except (configparser.NoSectionError, configparser.NoOptionError, ValueError) as e:
        logger.error(f"配置文件错误: {e}, 使用默认配置")
        # 返回默认配置
        ext_str = default_config['FileSettings']['allowed_extensions']
        return {
            'file_settings': {
                'allowed_extensions': {ext.strip().lower() for ext in ext_str.split(',') if ext.strip()},
                'min_size_kb': int(default_config['FileSettings']['min_size_kb'])
            },
            'oss_settings': {
                k: v if k in ['access_key_id', 'secret_access_key', 'server', 'bucket_name', 'osb_save_path']
                else int(v) if v.isdigit()
                else v
                for k, v in default_config['OSSSettings'].items()
            }
        }


def main():
    # 从配置文件加载所有设置
    config = load_config()
    allowed_extensions = config['file_settings']['allowed_extensions']
    min_size_kb = config['file_settings']['min_size_kb']
    oss_settings = config['oss_settings']

    # 打印配置信息（实际使用时可以移除）
    # logger.info("当前配置:")
    # logger.info(f"文件过滤: 允许的扩展名: {allowed_extensions}, 最小大小: {min_size_kb}KB")
    # logger.info("OSS配置:")
    # for k, v in oss_settings.items():
    #     if k not in ['secret_access_key']:  # 不打印敏感信息
    #         logger.info(f"  {k}: {v}")

    if len(sys.argv) > 1:
        logger.info("\n待上传文件列表:")
        valid_files = []

        for i, file_path in enumerate(sys.argv[1:], 1):
            if os.path.exists(file_path):
                # 获取文件信息
                file_name = os.path.basename(file_path)
                file_ext = os.path.splitext(file_path)[1].lower()
                file_size = os.path.getsize(file_path)
                file_size_kb = file_size / 1024

                # 检查条件
                size_ok = file_size_kb > min_size_kb
                ext_ok = file_ext in allowed_extensions

                if not ext_ok:
                    logger.error(f"{i}. 不支持的文件类型: {file_path} (类型: {file_ext})")
                elif not size_ok:
                    logger.error(f"{i}. 文件太小(≤{min_size_kb}KB): {file_path} (大小: {file_size_kb:.2f}KB)")
                else:
                    logger.info(f"{i}. 有效文件: {file_path} (类型: {file_ext}, 大小: {file_size_kb:.2f}KB)")
                    valid_files.append(file_path)
            else:
                logger.error(f"{i}. 无效路径: {file_path}")

        # 处理符合条件的文件
        if valid_files:
            logger.info("\n符合条件的文件:")
            for file in valid_files:
                logger.info(f"- {file}")

                # 开始上传OBS
                upload_obs_config = oss_settings.copy()
                upload_obs_config['local_file_path'] = file
                # print(upload_obs_config)
                upload_to_obs(**upload_obs_config)

        else:
            logger.warning("\n没有找到符合条件的文件")
    else:
        logger.info("请将文件拖拽到本程序上")
        logger.info(f"支持的文件类型: {', '.join(allowed_extensions)}")
        logger.info(f"最小文件大小: {min_size_kb}KB")
        logger.info("\n提示: 可以通过编辑 config.ini 文件修改配置")


if __name__ == "__main__":
    main()

# 执行上传
# if not upload_to_obs_with_progress(**config):
#     exit(1)
